package com.itheima.spring.cyclicDependency.core;


import com.itheima.spring.cyclicDependency.annitation.Component;
import com.itheima.spring.cyclicDependency.annitation.ComponentScan;
import com.itheima.spring.cyclicDependency.annitation.Scope;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 用一级缓存解决依赖循环问题
 */
public class AnnotationConfigApplicationContext {

    /**
     * beanDefinitionMap 存放多个Bean定义 其中key为BeanName,value为BeanDefinition
     */
    private final Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    /**
     * 单例池,如果创建的是单例对象则放入单例池中
     */
    private final Map<String, Object> singletonObjects = new HashMap<>(256);

    //早期Bean,还未被初始化以及属性赋值
    private final Map<String, Object> earlySingletonObjects = new ConcurrentHashMap<>(16);

    // 三级缓存
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    //循环依赖标识
    public final Set<String> singletonsCurrentlyInCreation = new HashSet<>();


    public AnnotationConfigApplicationContext(Class<?> myConfigClass) {
        scan(myConfigClass);
        refresh();
    }

    /**
     * 创建bean
     */
    private void refresh(){

        beanDefinitionMap.forEach((beanName,beanDefinition) ->{
            if ("singleton".equals(beanDefinition.getScope())) {
                try {
                    Object bean = createBean(beanName, beanDefinition);
                    singletonObjects.put(beanName, bean);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });
    }

    public Object createBean(String beanName,BeanDefinition beanDefinition){

        //先判断一级缓存中是否存在，这里是解决循环依赖的关键
        Object singleton = getSingleton(beanName);
        if(singleton != null){
            return singleton;
        }

        //正在创建
        singletonsCurrentlyInCreation.add(beanName);

        //用一级缓存解决循环依赖问题
        Class<?> aClass = beanDefinition.getType();
        Object instance = null;
        if(aClass.isAnnotationPresent(Component.class)){
            try{
                //无参构造创建Bean
                instance = aClass.getConstructor().newInstance();//无参构造创建Bean
                singletonObjects.put(beanName,instance);
                Field[] declaredFields = aClass.getDeclaredFields();
                for (Field field: declaredFields){
                    field.setAccessible(true);
                    //**在这里调用自身方法进行递归操作获取bean**
                    field.set(instance,createBean(field.getName(), beanDefinitionMap.get(field.getName())));
                }
            }catch (Exception e){
                e.printStackTrace();
            }


        }

        return instance;
    }

    public Object getSingleton(String beanName) {

        if(singletonObjects.containsKey(beanName)){
            return singletonObjects.get(beanName);
        }

        return null;
    }


    private void scan(Class<?> myConfigClass) {
        if (myConfigClass.isAnnotationPresent(ComponentScan.class)) {
            //如果当前配置类上存在注解@ComponentScan,则代表当前类为配置类
            ComponentScan componentScan = myConfigClass.getAnnotation(ComponentScan.class);
            //获取value内容  com.itheima.spring.cyclicDependency
            String path = componentScan.value();
            //将com.itheima.spring.cyclicDependency.bean-->com/itheima/spring/cyclicDependency/bean
            path = path.replace(".", "/");
            //获取当前类加载器
            ClassLoader classLoader = this.getClass().getClassLoader();
            URL resource = classLoader.getResource(path);
            assert resource != null;
            File file = new File(resource.getFile());
            if (file.isDirectory()) {
                File[] files = file.listFiles();
                assert files != null;
                for (File f : files) {
                    String absolutePath = f.getAbsolutePath();
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\", ".");
                    Class<?> aClass = null;
                    try {
                        //通过类加载器,加载当前类信息
                        aClass = classLoader.loadClass(absolutePath);
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                    }
                    assert aClass != null;
                    if (aClass.isAnnotationPresent(Component.class)) {
                        //如果当前类上存在存在@Component注解
                        Component component = aClass.getAnnotation(Component.class);
                        String beanName = component.value();
                        if ("".equals(beanName)) {
                            //如果没有给默认值,则使用类的首字母小写
                            beanName = Introspector.decapitalize(aClass.getSimpleName());
                        }
                        //扫描的目的就是获取BeanDefinition
                        BeanDefinition beanDefinition = new BeanDefinition();
                        beanDefinition.setType(aClass);
                        //判断是否存在Scope注解标记
                        if (aClass.isAnnotationPresent(Scope.class)) {
                            Scope scope = aClass.getAnnotation(Scope.class);
                            String value = scope.value();
                            beanDefinition.setScope(value);
                        } else {
                            beanDefinition.setScope("singleton");
                        }
                        beanDefinitionMap.put(beanName, beanDefinition);
                    }
                }
            }
        }
    }
    public Object getBean(String beanName) {
        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        }
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        //如果是获取的Bean在单例池中存在则从单例池中取,否则新建
        if ("singleton".equals(beanDefinition.getScope())) {
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean == null) {
                singletonBean = createBean(beanName, beanDefinition);
                singletonObjects.put(beanName, singletonBean);
            }
            return singletonBean;
        } else {
            // 原型Bean
            return createBean(beanName, beanDefinition);
        }
    }


    /**
     * 通过通过BeanName一级Bean class获取Bean对象
     *
     * @param beanName Bean别名
     * @param tClass   bean class类型
     * @param <T>      实例泛型
     * @return Bean对象
     */
    public <T> T getBean(String beanName, Class<T> tClass) {
        return tClass.cast(getBean(beanName));
    }


}
